package com.bora_bandeco;

import static com.bora_bandeco.CommonUtilities.DISPLAY_MESSAGE_ACTION;
import static com.bora_bandeco.CommonUtilities.SERVICO_INDISPONIVEL;
import static com.bora_bandeco.CommonUtilities.TAG;
import static com.bora_bandeco.CommonUtilities.URI_REGISTRAR_CONTATO;
import static com.bora_bandeco.CommonUtilities.URI_REMOVER_CONTATO;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;

import android.content.Context;
import android.content.Intent;
import android.util.Log;

import com.google.android.gcm.GCMRegistrar;

public class ServerUtilities {
	private static final int MAX_TENTATIVAS = 5;	
	private static final int TIMEOUT = 5;
    
    static void registrarContato(final Context context, String nome, String fotoCodificada, String numeroTelefone, final String regId) {
    	String serverUrl = URI_REGISTRAR_CONTATO;
        
    	Map<String, String> params = new HashMap<String, String>();
        params.put("regId", regId);
        params.put("nome", nome);
        params.put("foto", fotoCodificada);
        params.put("numero_telefone", numeroTelefone);
        
        for (int i = 1; i <= MAX_TENTATIVAS; i++) {
            Log.d(TAG, "Tentativa #" + i + " para registrar");
        
            try {
                post(serverUrl, params);
                GCMRegistrar.setRegisteredOnServer(context, true);
                return;
            } catch (IOException e) {
            	Log.e(TAG, "Falhou em registrar na tentativa " + i + ":" + e);
                
            	if (i == MAX_TENTATIVAS) {
                	// Notifica que não foi possível registrar pois houve erro no servidor
                	Intent novoIntent = new Intent(DISPLAY_MESSAGE_ACTION);
                	novoIntent.putExtra(SERVICO_INDISPONIVEL, true);
                    context.sendBroadcast(novoIntent);
                    return;
                }
                
                // Espera de 2 a 3 segundos antes da próxima tentativa
                Random random = new Random();
                long backoff = 2000 + random.nextInt(1000);
                try {
                    Log.d(TAG, "Espera por " + backoff + " ms antes de tentar novamente");
                    Thread.sleep(backoff);
                } catch (InterruptedException e1) {
                    Log.d(TAG, "Thread interrupted: abort remaining retries!");
                    Thread.currentThread().interrupt();
                    return;
                }
            }
        }
    }
 
    static void removerContato(final Context context, final String regId) {
    	String serverUrl = URI_REMOVER_CONTATO;
        
        Map<String, String> params = new HashMap<String, String>();
        params.put("regId", regId);

        try {
            post(serverUrl, params);
            GCMRegistrar.setRegisteredOnServer(context, false);
        } catch (IOException e) {
        	//
        }
    }
    
    static String buscarContatos(final Context context, List<String> numerosTelefone) {
    	String serverUrl = ContatosFetcher.URI;
        
    	Map<String, String> params = new HashMap<String, String>();
        
        for (int i = 0; i < numerosTelefone.size(); i++) {
        	params.put("numero_telefone[" + i + "]", numerosTelefone.get(i));
        }
        
        for (int i = 1; i <= MAX_TENTATIVAS; i++) {
            Log.d(TAG, "Tentativa #" + i + " para buscar contatos");
            
            try {
            	return post(serverUrl, params);
            } catch (IOException e) {
            	Log.e(TAG, "Falhou em buscar contatos na tentativa " + i + ":" + e);
                
            	if (i == MAX_TENTATIVAS) {
                	// Notifica que não foi possível buscar contatos pois houve erro no servidor
                	Intent novoIntent = new Intent(DISPLAY_MESSAGE_ACTION);
                	novoIntent.putExtra(SERVICO_INDISPONIVEL, true);
                    context.sendBroadcast(novoIntent);
                    return null;
                }
                
                // Espera de 2 a 3 segundos antes da próxima tentativa
                Random random = new Random();
                long backoff = 2000 + random.nextInt(1000);
                try {
                    Log.d(TAG, "Espera por " + backoff + " ms antes de tentar novamente");
                    Thread.sleep(backoff);
                } catch (InterruptedException e1) {
                    Log.d(TAG, "Thread interrupted: abort remaining retries!");
                    Thread.currentThread().interrupt();
                    return null;
                }
            }
        }
        return null;
    }
    
    static void enviarConvite(final Context context, String restaurante, long horario, int anfitriao, List<Contato> contatos) {
    	String serverUrl = ConviteSender.URI;
        
    	Map<String, String> params = new HashMap<String, String>();
        params.put("restaurante", restaurante);
        String horarioStr = String.valueOf(horario);
        params.put("horario", horarioStr);
        
        String anfitriaoStr = String.valueOf(anfitriao);
        params.put("id_anfitriao", anfitriaoStr);
        
        for (int i = 0; i < contatos.size(); i++) {
        	String idStr = String.valueOf(contatos.get(i).getId());
        	params.put("id_contato[" + i + "]", idStr);
        }
        
        for (int i = 1; i <= MAX_TENTATIVAS; i++) {
            Log.d(TAG, "Tentativa #" + i + " para enviar convite");
            
            try {
                post(serverUrl, params);
                return;
            } catch (IOException e) {
            	Log.e(TAG, "Falhou em enviar convite na tentativa " + i + ":" + e);
                
            	if (i == MAX_TENTATIVAS) {
                	// Notifica que não foi possível enviar convite pois houve erro no servidor
                	Intent novoIntent = new Intent(DISPLAY_MESSAGE_ACTION);
                	novoIntent.putExtra(SERVICO_INDISPONIVEL, true);
                    context.sendBroadcast(novoIntent);
                    return;
                }
                
                // Espera de 2 a 3 segundos antes da próxima tentativa
                Random random = new Random();
                long backoff = 2000 + random.nextInt(1000);
                try {
                    Log.d(TAG, "Espera por " + backoff + " ms antes de tentar novamente");
                    Thread.sleep(backoff);
                } catch (InterruptedException e1) {
                    Log.d(TAG, "Thread interrupted: abort remaining retries!");
                    Thread.currentThread().interrupt();
                    return;
                }
            }
        }
    }
    
    static void enviarResposta(final Context context, int idConvite, int idContato, int resposta) {
    	String serverUrl = ConviteResponder.URI;
        
    	Map<String, String> params = new HashMap<String, String>();
        
        String idConviteStr = String.valueOf(idConvite);
        params.put("id_convite", idConviteStr);
        
        String idContatoStr = String.valueOf(idContato);
        params.put("id_contato", idContatoStr);
        
        String respostaStr = String.valueOf(resposta);
        params.put("resposta", respostaStr);
        
        for (int i = 1; i <= MAX_TENTATIVAS; i++) {
            Log.d(TAG, "Tentativa #" + i + " para enviar resposta");
            
            try {
                post(serverUrl, params);
                return;
            } catch (IOException e) {
        		Log.e(TAG, "Falhou em enviar resposta na tentativa " + i + ":" + e);
                
            	if (i == MAX_TENTATIVAS) {
                	// Notifica que não foi possível enviar resposta pois houve erro no servidor
                	Intent novoIntent = new Intent(DISPLAY_MESSAGE_ACTION);
                	novoIntent.putExtra(SERVICO_INDISPONIVEL, true);
                    context.sendBroadcast(novoIntent);
                    return;
                }
                
                // Espera de 2 a 3 segundos antes da próxima tentativa
                Random random = new Random();
                long backoff = 2000 + random.nextInt(1000);
                try {
                    Log.d(TAG, "Espera por " + backoff + " ms antes de tentar novamente");
                    Thread.sleep(backoff);
                } catch (InterruptedException e1) {
                    Log.d(TAG, "Thread interrupted: abort remaining retries!");
                    Thread.currentThread().interrupt();
                    return;
                }
            }
        }
    }
 
    /**
     * Issue a POST request to the server.
     *
     * @param endpoint POST address.
     * @param params request parameters.
     *
     * @throws IOException propagated from POST.
     */
    private static String post(String endpoint, Map<String, String> params) throws IOException {    
        URL url;
        String responseString = null;
        try {
            url = new URL(endpoint);
        } catch (MalformedURLException e) {
            throw new IllegalArgumentException("invalid url: " + endpoint);
        }
        StringBuilder bodyBuilder = new StringBuilder();
        Iterator<Entry<String, String>> iterator = params.entrySet().iterator();
        // constructs the POST body using the parameters
        while (iterator.hasNext()) {
            Entry<String, String> param = iterator.next();
            bodyBuilder.append(param.getKey()).append('=').append(param.getValue());
            
            if (iterator.hasNext()) {
                bodyBuilder.append('&');
            }
        }
        
        String body = bodyBuilder.toString().replace("+", "%2B");
        Log.v(TAG, "Posting '" + body + "' to " + url);
        byte[] bytes = body.getBytes();
        HttpURLConnection conn = null;
        try {
            Log.e("URL", "> " + url);
            conn = (HttpURLConnection) url.openConnection();
            conn.setConnectTimeout(TIMEOUT*1000);
            conn.setReadTimeout(TIMEOUT*1000);
            conn.setDoOutput(true);
            conn.setUseCaches(false);
            conn.setFixedLengthStreamingMode(bytes.length);
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
            // post the request
            OutputStream out = conn.getOutputStream();
            out.write(bytes);
            out.close();
            // handle the response
            int status = conn.getResponseCode();
            if (status != 200) {
              throw new IOException("Post failed with error code " + status);
            }
        } finally {
            if (conn != null) {
            	//Get Response	
                InputStream is = conn.getInputStream();
                BufferedReader rd = new BufferedReader(new InputStreamReader(is));
                String line;
                StringBuffer response = new StringBuffer(); 
                while((line = rd.readLine()) != null) {
                  response.append(line);
                  response.append('\n');
                }
                responseString = response.toString();
                rd.close();
                conn.disconnect();
            }
        }
        
        return responseString;
      }
}
